
#include "info.h"

Err rp_rstatd_info(Info *info)
{
	static char fname[] = "rp_rstatd_info()";
	static char msg[] = "fatal error";
	Err err;
	static int is_first_time = 1;
	FLOG_IN(fname);

	if (is_first_time)
	{
		err = rp_rstatd_info_sys(info);
		err = rp_rstatd_info_net(info);
		err = rp_rstatd_info_cpu(info);
		info->fv = rp_rstatd_fv_init(1024);
		is_first_time = 0;
	}
	err = rp_rstatd_fv_destory(info->fv);
	info->fv = rp_rstatd_fv_init(1024);
	err = rp_rstatd_info_file(info->fv, config.rpdir);
	err = rp_rstatd_info_load(info);
	err = rp_rstatd_info_score(&(info->sb_info));
	err = rp_rstatd_info_mem(info);
	err = rp_rstatd_info_disk(info)

	FLOG_OUT(fname);
	return err;
}

Err rp_rstatd_info_load(Info *info)
{

}

Err rp_rstatd_info_net(Info *info)
{
	char fname[] = "rp_rstatd_info_net()";
	char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	/*	Get local IP address	*/		// TODO: multiple eth suport
       int sock;
       struct sockaddr_in sin;
       struct ifreq ifr;
       
       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
       {
		rp_log(LOG_CRITICAL, fname, msg);
		return E_ERROR;
       }
        
       strncpy(ifr.ifr_name, ETH_ID, IFNAMSIZ);
       ifr.ifr_name[IFNAMSIZ - 1] = 0;
       
       if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
       {
		rp_log(LOG_CRITICAL, fname, msg);
		return E_ERROR;
       }

       memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
       info->ip = inet_ntoa(sin.sin_addr);

	/*	Get the local hostname	*/
	memset(info->hostname, 0, MAX_HOSTNAME_LENGTH);
       if (gethostname(info->hostname, MAX_HOSTNAME_LENGTH)) 
	{
		rp_log(LOG_CRITICAL, fname, msg);
		return E_ERROR;		
	}

	/*	Get rpointd's port number	*/
	info->port = ntohs(config.addr_rindexd.sin_port);

	FLOG_OUT(fname);
       return E_OK;
}

Err rp_rstatd_info_disk(Info *info)
{
	static char fname[] = "rp_rstatd_info_disk()";
	static char msg[] = "fatal error";
	Err err;
	struct statfs fs;
	FLOG_IN(fname);

#ifdef _STATFS
	if (!statfs(config.rpdir, &fs))
	{
		info->disk_max	= (unsigned long)floor(fs.f_bsize/1024*fs.f_blocks);
		info->disk_free	= (unsigned long)floor(fs.f_bsize/1024*fs.f_bavail);
		return E_OK;
	}
	rp_log(LOG_WARNING, fname, "Cannot get the file system stat by calling statfs(). Error: %s", strerror(errno));
#else	
	/*	get from /proc   */
	// TODO: read /proc
#endif	// _STATFS

	FLOG_OUT(fname);
	return err;
}

Err rp_rstatd_info_cpu(Info *info)
{
	static char fname[] = "rp_rstatd_info_cpu()";
	static char msg[] = "fatal error";
	Err err;
	FILE *fp;
	char buf[1024], *p;
	int ncpu = 0;
	FLOG_IN(fname);

  	if (!(fp = fopen("/proc/cpuinfo", "rb")))
  	{
		rp_log(LOG_WARNING, fname, "Cannot open the file /proc/cpuinfo. Error: %s", strerror(errno));
		return E_ERROR;
	}

	memset(buf, 0, 1024);
	while (fgets(buf, sizeof(buf)-1, fp))
	{
		if ((p = strstr(buf, "cpu cores")) == NULL)  continue;
		if ((p = strstr(p, ":")) == NULL)  continue;
		ncpu += atoi(p+2);
	}
	info->ncpu = ncpu;
	fclose(fp);
	
	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_info_mem(Info *info)
{
	static char fname[] = "rp_rstatd_info_mem()";
	static char msg[] = "fatal error";
	Err err;
	char buf[1024], *p;
	int mem_max = 0;
	int mem_free = 0;
	FILE *fp;
	FLOG_IN(fname);

  	if (!(fp = fopen("/proc/meminfo", "rb")))
  	{
		rp_log(LOG_WARNING, fname, "Cannot open the file /proc/meminfo. Error: %s", strerror(errno));
		return E_ERROR;
	}
	
	memset(buf, 0, 1024);
	while (fgets(buf, sizeof(buf)-1, fp))
	{
		if ((p = strstr(buf, "MemTotal")) == NULL) continue;
		if ((p = strstr(p, ":")) == NULL)  continue;
		mem_max = atoi(p+2);
		break;
	}
	fseek(fp, 0, SEEK_SET);
	while (fgets(buf, sizeof(buf)-1, fp))
	{
		if ((p = strstr(buf, "MemFree")) == NULL) continue;
		if ((p = strstr(p, ":")) == NULL)  continue;
		mem_free = atoi(p+2);
		break;
	}	
	info->mem_max = mem_max;
	info->mem_free = mem_free;
	fclose(fp);

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_info_sys(Info *info)
{
	static char fname[] = "rp_rstatd_info_sys()";
	static char msg[] = "fatal error";
	Err err;
	char buf[1024];
	FILE *fp;
	FLOG_IN(fname);

  	if (!(fp = fopen("/proc/version", "rb")))
  	{
		rp_log(LOG_WARNING, fname, "Cannot open the file /proc/version. Error: %s", strerror(errno));
		return E_ERROR;
	}
	
	memset(buf, 0, 1024);
	fgets(buf, sizeof(buf)-1, fp);
	info->os = strdup(buf);
	fclose(fp);

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_info_score(Scoreboard *sb_info)
{
	char fname[] = "rp_rstatd_info_score()";
	char msg[] = "fatal error";
	Err err;
	Worker *worker_live;
	Worker *worker_info;

	worker_live = global.sb_live.head;
	worker_info = sb_info->head;

	while (worker_live)
	{
	        if (worker_live->score->state == SS_FAILED  || worker_live->score->state == SS_IDLE)
		{	
			if (worker_info) 	// reuse the existing node
			  {
				worker_info->pid = worker_live->pid;
				worker_info->score->state = rp_score_get_state(worker_live->score);
				sb_info->tail = worker_info;
				
				worker_info = worker_info->next;
				worker_live = worker_live->next;
				continue;
			} 
			
			worker_info = (Worker*)malloc(sizeof(Worker));
			worker_info->score = (Score*)malloc(sizeof(Score));
			worker_info->pid = worker_live->pid;
			worker_info->score->state = rp_score_get_state(worker_live->score);
			
			if (!sb_info->total++)
			{
				worker_info->next = NULL;
				worker_info->prev = NULL;
				sb_info->head = worker_info;
				sb_info->tail = worker_info;
				
				worker_info = NULL;		
				worker_live = worker_live->next;
				continue;
			}
			
			worker_info->next = NULL;
			worker_info->prev = sb_info->tail;
			sb_info->tail->next = worker_info;
			sb_info->tail = worker_info;
			
			worker_info = NULL;
			worker_live = worker_live->next;
			continue;			
		}
		worker_live = worker_live->next;
	}

	while (worker_info)
	{
		if (worker_info->next)	
		{
			worker_info = worker_info->next;
			free(worker_info->prev->score);
			free(worker_info->prev);
		} else {
			free(worker_info->score);
			free(worker_info);
		}
		sb_info->total--;
	}

	if (sb_info->total == 0)
	{
		sb_info->head = NULL;
		sb_info->tail = NULL;
	}

	return E_OK;
}

Err rp_rstatd_info_file(Fvector *fv, char *path)
{
	static char fname[] = "rp_rstatd_info_file()";
	static char msg[] = "fatal error";
	Err err;
	DIR *dir;
	static File file;
	struct stat st;
	struct dirent *dirent;
	static char buf[MAX_FILENAME_LENGTH];
	FLOG_IN(fname);

	if (stat(path, &st) < 0)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot get the file stat %s. Error: %s", path, strerror(errno));
		return E_ERROR;
	}
	if (!S_ISDIR(st.st_mode))
	{
		rp_log(LOG_CRITICAL, fname, "Invalid rally directory: %s", path);
		return E_INVALID;
	}

	if ((dir = opendir(path)) == NULL)
	{
		rp_log(LOG_CRITICAL, fname, "Cannot open the directory: %s. Error: %s", path, strerror(errno));
		return E_ERROR;
	}

	while (dirent = readdir(dir))
	{
		if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..")) continue;
		memset(buf, 0, MAX_FILENAME_LENGTH);
		sprintf(buf, "%s/%s", path, dirent->d_name);
		if (stat(buf, &st) < 0)
		{
			rp_log(LOG_WARNING, fname, "Cannot get the file stat %s. Error: %s", buf, strerror(errno));
			continue;
		}
		if (S_ISREG(st.st_mode) || S_ISBLK(st.st_mode))
		{
			memset(file.filename, 0, MAX_FILENAME_LENGTH);
			strcpy(file.filename, dirent->d_name);
			file.filesize = st.st_size;
			err = rp_rstatd_fv_add(fv, &file);
		} else if (S_ISDIR(st.st_mode))
		{
			err = rp_rstatd_info_file(fv, buf);
		} else {
			rp_log(LOG_WARNING, fname, "Unsupported filetype: %s/%s", path, buf);
		}
	}
	
	free(dirent);
	closedir(dir);	
	FLOG_OUT(fname);
	return err;
}

Fvector *rp_rstatd_info_file_delta(Fvector *fv_new, Fvector * fv_old)
{
	static char fname[] = "rp_rstatd_info_file_delta";
	static char msg[] = "fatal error";
	Err err;
	Fvector *fv_delta;
	File *file;
	FLOG_IN(fname);

	if (!fv_old)
	{
		return fv_new;
	}

	int i;
	for (i=0; i<fv_new->size - fv_new->free; i++)
	{
		//file = rp_rstatd_fv_search(fv_old, )
	}


	FLOG_OUT(fname);
	return fv_delta;
}


Fvector *rp_rstatd_fv_init(int size)
{
	static char fname[] = "rp_rstatd_fv_init()";
	static char msg[] = "fatal error";
	Err err;
	Fvector *fv;
	FLOG_IN(fname);

	fv = (Fvector *)malloc(sizeof(Fvector));
	fv->files = (File *)calloc(size, sizeof(File));
	fv->size = size;
	fv->free = size;
	
	FLOG_OUT(fname);
	return fv;
}	

Err rp_rstatd_fv_add(Fvector *fv, File *file)
{
	static char fname[] = "rp_rstatd_fv_add()";
	static char msg[] = "fatal error";
	Err err;
	File *files;
	FLOG_IN(fname);

	if (!fv || !fv->files)
	{
		rp_log(LOG_DEBUG, fname, "Cannot add new item: Fvectore uninitialized");
		return E_INVALID;
	}
	
	if (!fv->free)
	{	// vector expand
		files = (File *)calloc(fv->size*2, sizeof(File));
		memcpy(files, fv->files, fv->size*sizeof(File));
		fv->free = fv->size-1;
		fv->size *= 2;
	}
	strncpy((fv->files+(fv->size-fv->free))->filename, file->filename, MAX_FILENAME_LENGTH);
	(fv->files+(fv->size-fv->free))->filesize = file->filesize;
	fv->free--;

	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_fv_remove(Fvector *fv, char *filename)
{
	static char fname[] = "rp_rstatd_fv_remove()";
	static char msg[] = "fatal error";
	Err err;
	File *file;
	int found = 0;
	FLOG_IN(fname);

	if (!fv || !fv->files)
	{
		rp_log(LOG_DEBUG, fname, "Cannot add new item: Fvectore uninitialized");
		return E_INVALID;
	}
	
	int i;
	for (i=0; i<fv->size-fv->free; i++)
	{
		file = fv->files+i;
		if (!strncmp(file->filename, filename, MAX_FILENAME_LENGTH))  
		{
			found = 1;
			break;
		}
	}
	if (!found)
	{
		rp_log(LOG_DEBUG, fname, "Cannot remove item: no matched item found. File: %s", filename);
		return E_ERROR;
	}
	
	memcpy(file, file+1, (fv->size-fv->free-i-1)*sizeof(File));
	file = fv->files+fv->size-fv->free-1;
	memset(file->filename, 0, MAX_FILENAME_LENGTH);
	file->filesize = 0;
	fv->free++;
	if (fv->free > fv->size/2)
	{	// Vector shrink
		file = (File *)calloc(fv->size/2, sizeof(File));
		memcpy(file, fv->files, (fv->size-fv->free)*sizeof(File));
		free(fv->files);
		fv->files = file;
		fv->size/= 2;
	}
	
	FLOG_OUT(fname);
	return E_OK;
}

Err rp_rstatd_fv_destory(Fvector *fv)
{
	static char fname[] = "rp_rstatd_fv_destroy()";
	static char msg[] = "fatal error";
	Err err;
	FLOG_IN(fname);

	if (!fv || !fv->files)
	{
		rp_log(LOG_DEBUG, fname, "Cannot add new item: Fvectore uninitialized");
		return E_INVALID;
	}

	free(fv->files);
	fv->size = 0;
	fv->free = 0;

	FLOG_OUT(fname);
	return E_OK;
}


